package org.budo.graph.service.util;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.ibatis.builder.StaticSqlSource;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.scripting.defaults.RawSqlSource;
import org.apache.ibatis.scripting.xmltags.DynamicSqlSource;
import org.apache.ibatis.scripting.xmltags.ForEachSqlNode;
import org.apache.ibatis.scripting.xmltags.MixedSqlNode;
import org.apache.ibatis.scripting.xmltags.SetSqlNode;
import org.apache.ibatis.scripting.xmltags.SqlNode;
import org.apache.ibatis.scripting.xmltags.StaticTextSqlNode;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSessionFactory;
import org.budo.support.fastjson.Fastjson;
import org.budo.support.lang.util.ReflectUtil;
import org.budo.support.spring.util.SpringUtil;
import org.springframework.context.ApplicationContext;

/**
 * @author limingwei
 */
public class MybatisMapperUtil {
    private static final boolean HAS_MYBATIS = ReflectUtil.hasClass("org.apache.ibatis.session.SqlSessionFactory");

    public static String writeMybatisMapperResponse(ApplicationContext applicationContext, String url) {
        if (!HAS_MYBATIS) {
            return "NOT_HAS_MYBATIS";
        }

        List<SqlSessionFactory> sessionFactories = SpringUtil.getBeanListByType(applicationContext, SqlSessionFactory.class);
        if (null == sessionFactories || sessionFactories.isEmpty()) {
            return "SQL_SESSION_FACTORY_IS_NULL";
        }

        List<Object> list = new ArrayList<Object>();
        for (SqlSessionFactory sessionFactory : sessionFactories) {
            Map<String, Object> sessionFactoryMap = sessionFactoryMap(sessionFactory);
            list.add(sessionFactoryMap);
        }

        return Fastjson.toJSONString(list);
    }

    private static Map<String, Object> sessionFactoryMap(SqlSessionFactory sessionFactory) {
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("sessionFactory", sessionFactory.toString());

        Configuration configuration = sessionFactory.getConfiguration();
        Collection<MappedStatement> mappedStatements = configuration.getMappedStatements();

        Object _mappedStatements_2 = _mappedStatements_2(mappedStatements);
        map.put("mappedStatements", _mappedStatements_2);

        return map;
    }

    private static Object _mappedStatements_2(Collection<MappedStatement> mappedStatements) {
        List<Object> list = new ArrayList<Object>();
        for (Object each : mappedStatements) {
            Object _mappedStatements_3 = _mappedStatements_3(each);
            list.add(_mappedStatements_3);
        }

        return list;
    }

    private static Object _mappedStatements_3(Object each) {
        Map<String, Object> map = new HashMap<String, Object>();

        if (null == each || !(each instanceof MappedStatement)) {
            map.put("type", each.getClass().getName());
            return map;
        }

        MappedStatement mappedStatement = (MappedStatement) each;

        map.put("id", mappedStatement.getId());
        map.put("timeout", mappedStatement.getTimeout());

        SqlSource sqlSource = mappedStatement.getSqlSource();
        Map<String, Object> sqlSourceMap = sqlSource(sqlSource);

        map.putAll(sqlSourceMap);

        return map;
    }

    private static Map<String, Object> sqlSource(SqlSource sqlSource) {
        if (sqlSource instanceof DynamicSqlSource) {
            DynamicSqlSource dynamicSqlSource = (DynamicSqlSource) sqlSource;
            SqlNode fieldValue = (SqlNode) ReflectUtil.getFieldValue(dynamicSqlSource, "rootSqlNode");

            Map<String, Object> map = new HashMap<String, Object>();
            map.put("type", "DynamicSqlSource");
            map.put("rootSqlNode", _SqlNode_1(fieldValue));
            return map;
        }

        if (sqlSource instanceof RawSqlSource) {
            RawSqlSource rawSqlSource = (RawSqlSource) sqlSource;
            SqlSource sqlSource2 = (SqlSource) ReflectUtil.getFieldValue(rawSqlSource, "sqlSource");

            Map<String, Object> map = new HashMap<String, Object>();
            map.put("type", "RawSqlSource");
            map.put("sqlSource", sqlSource(sqlSource2));
            return map;
        }

        if (sqlSource instanceof StaticSqlSource) {
            StaticSqlSource staticSqlSource = (StaticSqlSource) sqlSource;
            String sql = (String) ReflectUtil.getFieldValue(staticSqlSource, "sql");

            Map<String, Object> map = new HashMap<String, Object>();
            map.put("type", "StaticSqlSource");
            map.put("sql", sql.replace("\n", ""));
            return map;
        }

        Map<String, Object> map = new HashMap<String, Object>();
        map.put("type", sqlSource.getClass().getName());
        return map;
    }

    private static Object _SqlNode_1(SqlNode sqlNode) {
        if (sqlNode instanceof MixedSqlNode) {
            MixedSqlNode mixedSqlNode = (MixedSqlNode) sqlNode;
            List<SqlNode> contents = (List<SqlNode>) ReflectUtil.getFieldValue(mixedSqlNode, "contents");

            List<Object> list = new ArrayList<Object>();

            for (SqlNode each : contents) {
                Object _SqlNode_1 = _SqlNode_1(each);
                list.add(_SqlNode_1);
            }

            return list;
        }

        if (sqlNode instanceof StaticTextSqlNode) {
            StaticTextSqlNode staticTextSqlNode = (StaticTextSqlNode) sqlNode;
            String fieldValue = (String) ReflectUtil.getFieldValue(staticTextSqlNode, "text");
            return fieldValue.replace("\n", "");
        }

        if (sqlNode instanceof ForEachSqlNode) {
            Map<String, Object> map = new HashMap<String, Object>();
            map.put("type", "ForEachSqlNode");
            return map;
        }

        if (sqlNode instanceof SetSqlNode) {
            Map<String, Object> map = new HashMap<String, Object>();
            map.put("type", "SetSqlNode");
            return map;
        }

        Map<String, Object> map = new HashMap<String, Object>();
        map.put("type", sqlNode.getClass().getName());
        return map;

//        TextSqlNode
//        IfSqlNode
//        TrimSqlNode
//        WhereSqlNode
//        ChooseSqlNode
    }
}
